%{
  open Types
  let get_error() = 
     let symbol = Parsing.symbol_start_pos () in
     let line = symbol.Lexing.pos_lnum in
     let pos = symbol.Lexing.pos_cnum - symbol.Lexing.pos_bol in
       (line, pos)
%}


%token LPA RPA ASSIGN STOP FH EOF
%token ADD REM MUL DIV POW CONCAT
%token OR AND
%token IF THEN ELSE
%token <string> OP
%token <string> NUM
%token <string> FUN STRING
%token <bool> BOOL


%start eval
%type <Types.prog option> eval

%left ADD REM 
%left MUL DIV
%right POW CONCAT

%left OR
%left AND
%%

eval:
  | file EOF             { Some $1 }
  | error                { Helper.fail ("Syntax error", "Erreur de syntaxe")  (get_error()) }

file:
  | def FH file            { $1 :: $3 }
  | def                    { [$1] }
def:
  | FUN ASSIGN expr         { Def ($1, $3) }
  | FUN args ASSIGN expr    { DFun ($1, $2, $4) }
  | expr                    { Expr $1 }

expr:
  | elem STOP expr { $1@$3 }
  | elem           { $1 }

elem:
  | REM aux_elem { [Node ((FUN "-", get_error()), [[Leaf (NUM (Bignum.string_to_num ("0")), get_error())]; $2])] }
  | aux_elem {$1}
aux_elem:
  | atom { [$1] }
  | func { [$1] }
  | LPA expr RPA  { $2 }

atom:
  | FUN             { Leaf (VAR $1, get_error())  }
  | NUM             { Leaf (NUM (Bignum.string_to_num $1), get_error())  }
  | STRING          { Leaf (STRING $1, get_error())  }
  | BOOL            { Leaf (BOOL $1, get_error()) }
  | LPA RPA         { Leaf  (UNIT (), get_error()) }

num:
  | REM NUM         { Leaf (NUM (Bignum.string_to_num ("-"^$2)), get_error())  }
  | REM FUN         { Node ((FUN "-", get_error()), [[Leaf (NUM (Bignum.string_to_num ("0")), get_error())]; [Leaf (VAR $2, get_error())]]) }
  | NUM             { Leaf (NUM (Bignum.string_to_num $1), get_error())  }
func:
  | elem ADD elem       { Node ((FUN "+", get_error()), [$1;$3]) }
  | elem REM elem       { Node ((FUN "-", get_error()), [$1;$3]) }
  | elem MUL elem       { Node ((FUN "*", get_error()), [$1;$3]) }
  | elem DIV elem       { Node ((FUN "/", get_error()), [$1;$3]) }
  | elem POW elem       { Node ((FUN "^", get_error()), [$1;$3]) }
  | elem CONCAT elem    { Node ((FUN "++", get_error()), [$1;$3]) }
  | elem AND elem       { Node ((FUN "&&", get_error()), [$1;$3]) }
  | elem OR elem        { Node ((FUN "||", get_error()), [$1;$3]) }
  | elem OP elem        { Node ((FUN $2, get_error()), [$1;$3]) }

  | FUN list_fargs        { Node ((FUN $1, get_error()), $2) }
  | IF elem THEN elem ELSE elem  { Node ((FUN "if", get_error()), [$2;$4;$6]) }
 
fargs:
  | atom                { [$1] }
  | LPA expr RPA        {  $2  }

list_fargs:
  | fargs list_fargs   { $1::$2 }
  | fargs              { [$1] }

args:
  | aux_args { $1 }
  | LPA RPA  { [""] }

aux_args:
  | FUN aux_args     { $1::$2 }
  | FUN /* Empty */  { [$1] }
